home *** CD-ROM | disk | FTP | other *** search
/ Programmer Power Tools / Programmer Power Tools.iso / editor / j414src.arc / DISP.C < prev    next >
C/C++ Source or Header  |  1989-10-10  |  31KB  |  1,458 lines

  1. /***************************************************************************
  2.  * This program is Copyright (C) 1986, 1987, 1988 by Jonathan Payne.  JOVE *
  3.  * is provided to you without charge, and with no warranty.  You may give  *
  4.  * away copies of JOVE, including sources, provided that this notice is    *
  5.  * included in all the files.                                              *
  6.  ***************************************************************************/
  7.  
  8. #include "jove.h"
  9. #include "ctype.h"
  10. #include "termcap.h"
  11. #include "chars.h"
  12. #include "fp.h"
  13. #include "disp.h"
  14. #if defined(IPROCS)
  15. # include "iproc.h"
  16. #endif
  17.  
  18. #ifdef MAC
  19. # include "mac.h"
  20. #else
  21. # ifdef    STDARGS
  22. #  include <stdarg.h>
  23. # else
  24. #  include <varargs.h>
  25. # endif
  26. # include <sys/stat.h>
  27. #endif
  28.  
  29. #include <signal.h>
  30.  
  31. private void
  32. #ifdef ID_CHAR
  33.     DeTab proto((int, char *, char *, size_t, int)),
  34.     DelChar proto((int, int, int)),
  35.     InsChar proto((int, int, int, char *)),
  36. #endif
  37.     DoIDline proto((int)),
  38.     do_cl_eol proto((int)),
  39.     ModeLine proto((Window *)),
  40.     GotoDot proto((void)),
  41.     UpdLine proto((int)),
  42.     UpdWindow proto((Window *, int));
  43.  
  44. #ifdef MSDOS
  45. extern void    dobell proto((int x));
  46. #else
  47. private void    dobell proto((int x));
  48. #endif
  49.  
  50. private int
  51. #ifdef ID_CHAR
  52.     IDchar proto ((char *, int, int)),
  53.     NumSimilar proto ((char *, char *, int)),
  54.     IDcomp proto ((char *, char *, int)),
  55.     OkayDelete proto ((int, int, int)),
  56.     OkayInsert proto ((int, int)),
  57. #endif
  58.     AddLines proto((int, int)),
  59.     DelLines proto((int, int)),
  60.     UntilEqual proto((int));
  61.  
  62.  
  63.  
  64. int    DisabledRedisplay = NO;
  65.  
  66. /* Kludge windows gets called by the routines that delete lines from the
  67.    buffer.  If the w->w_line or w->w_top are deleted and this procedure
  68.    is not called, the redisplay routine will barf. */
  69.  
  70. void
  71. ChkWindows(line1, line2)
  72. Line    *line1;
  73. register Line    *line2;
  74. {
  75.     register Window    *w = fwind;
  76.     register Line    *lp;
  77.  
  78.     do {
  79.         for (lp = line1->l_next; lp != line2->l_next; lp = lp->l_next) {
  80.             if (lp == w->w_top)
  81.                 w->w_flags |= W_TOPGONE;
  82.             if (lp == w->w_line)
  83.                 w->w_flags |= W_CURGONE;
  84.         }
  85.         w = w->w_next;
  86.     } while (w != fwind);
  87. }
  88.  
  89. private int    RingBell;    /* So if we have a lot of errors ...
  90.                   ring the bell only ONCE */
  91.  
  92. void
  93. redisplay()
  94. {
  95.     register Window    *w = fwind;
  96.     int    lineno,
  97.         done_ID = NO,
  98.         i;
  99.     register struct scrimage    *des_p,
  100.                     *phys_p;
  101.  
  102.     if (DisabledRedisplay == YES)
  103.         return;
  104.     curwind->w_line = curwind->w_bufp->b_dot;
  105.     curwind->w_char = curwind->w_bufp->b_char;
  106. #ifdef MAC
  107.     InputPending = 0;
  108. #else
  109.     if ((InputPending = charp()) != '\0')    /* calls CheckEvent, which could */
  110.         return;    /* result in a call to rediplay(). We don't want that. */
  111. #endif
  112. #ifdef JOB_CONTROL
  113.     if (UpdFreq)
  114.         SigHold(SIGALRM);
  115. #endif
  116.     if (RingBell) {
  117.         dobell(1);
  118.         RingBell = 0;
  119.     }
  120.     AbortCnt = BufSize;        /* initialize this now */
  121.     if (UpdMesg)
  122.         DrawMesg(YES);
  123.  
  124.     for (lineno = 0, w = fwind; lineno < ILI; w = w->w_next) {
  125.         UpdWindow(w, lineno);
  126.         lineno += w->w_height;
  127.     }
  128.  
  129.     UpdModLine = 0;    /* Now that we've called update window, we can
  130.                assume that the modeline will be updated.  But
  131.                if while redrawing the modeline the user types
  132.                a character, ModeLine() is free to set this on
  133.                again so that the modeline will be fully drawn
  134.                at the next redisplay. */
  135.  
  136.     des_p = DesiredScreen;
  137.     phys_p = PhysScreen;
  138.     for (i = 0; i < ILI; i++, des_p++, phys_p++) {
  139.         if (!done_ID && (des_p->s_id != phys_p->s_id)) {
  140.             DoIDline(i);
  141.             done_ID = YES;
  142.         }
  143.         if ((des_p->s_flags & (DIRTY | L_MOD)) ||
  144.             (des_p->s_id != phys_p->s_id) ||
  145.             (des_p->s_vln != phys_p->s_vln) ||
  146.             (des_p->s_offset != phys_p->s_offset))
  147.             UpdLine(i);
  148.         if (InputPending)
  149.             goto ret;
  150.     }
  151.  
  152.     if (Asking) {
  153.         Placur(LI - 1, min(CO - 2, calc_pos(mesgbuf, Asking)));
  154.             /* Nice kludge */
  155.         flusho();
  156.     } else
  157.         GotoDot();
  158. ret:
  159. #ifdef JOB_CONTROL
  160.     if (UpdFreq)
  161.         SigRelse(SIGALRM);
  162. #else
  163.     ;    /* yuck */
  164. #endif
  165. #ifdef MAC
  166.     if(Windchange) docontrols();
  167. #endif /* MAC */
  168. }
  169.  
  170. #ifndef IBMPC
  171. private void
  172. dobell(n)
  173. int    n;
  174. {
  175.     while (--n >= 0) {
  176. #ifndef MAC
  177.         if (VisBell && VB)
  178.             putstr(VB);
  179.         else
  180.             putpad(BL, 1);
  181. #else
  182.         SysBeep(5);
  183. #endif
  184.     }
  185.     flusho();
  186. }
  187. #endif /* IBMPC */
  188.  
  189. /* find_pos() returns the position on the line, that C_CHAR represents
  190.    in LINE */
  191.  
  192. int
  193. find_pos(line, c_char)
  194. Line    *line;
  195. int    c_char;
  196. {
  197.     return calc_pos(lcontents(line), c_char);
  198. }
  199.  
  200. int
  201. calc_pos(lp, c_char)
  202. register char    *lp;
  203. register int    c_char;
  204. {
  205.     register int    pos = 0;
  206.     register int    c;
  207.  
  208.  
  209.     while ((--c_char >= 0) && ((c = *lp++) & CHARMASK) != 0) {
  210.         if (c == '\t')
  211.             pos += (tabstop - (pos % tabstop));
  212.         else if (isctrl(c))
  213.             pos += 2;
  214.         else
  215.             pos += 1;
  216.     }
  217.     return pos;
  218. }
  219.  
  220. int    UpdModLine = 0,
  221.     UpdMesg = 0;
  222.  
  223. private void
  224. DoIDline(start)
  225. int    start;
  226. {
  227.     register struct scrimage    *des_p = &DesiredScreen[start];
  228.     struct scrimage    *phys_p = &PhysScreen[start];
  229.     register int    i,
  230.             j;
  231.  
  232.     /* Some changes have been made.  Try for insert or delete lines.
  233.        If either case has happened, Addlines and/or DelLines will do
  234.        necessary scrolling, also CONVERTING PhysScreen to account for the
  235.        physical changes.  The comparison continues from where the
  236.        insertion/deletion takes place; this doesn't happen very often,
  237.        usually it happens with more than one window with the same
  238.        buffer. */
  239.  
  240.     if (!CanScroll)
  241.         return;        /* We should never have been called! */
  242.  
  243.     for (i = start; i < ILI; i++, des_p++, phys_p++)
  244.         if (des_p->s_id != phys_p->s_id)
  245.             break;
  246.  
  247.     for (; i < ILI; i++) {
  248.         for (j = i + 1; j < ILI; j++) {
  249.             des_p = &DesiredScreen[j];
  250.             phys_p = &PhysScreen[j];
  251.             if (des_p->s_id != 0 && des_p->s_id == phys_p->s_id)
  252.                 break;
  253.             if (des_p->s_id == PhysScreen[i].s_id) {
  254.                 if (des_p->s_id == 0)
  255.                     continue;
  256.                 if (AddLines(i, j - i)) {
  257.                     DoIDline(j);
  258.                     return;
  259.                 }
  260.                 break;
  261.             }
  262.             if ((des_p = &DesiredScreen[i])->s_id == phys_p->s_id) {
  263.                 if (des_p->s_id == 0)
  264.                     continue;
  265.                 if (DelLines(i, j - i)) {
  266.                     DoIDline(i);
  267.                     return;
  268.                 }
  269.                 break;
  270.             }
  271.         }
  272.     }
  273. }
  274.  
  275. /* Make DesiredScreen reflect what the screen should look like when we are done
  276.    with the redisplay.  This deals with horizontal scrolling.  Also makes
  277.    sure the current line of the Window is in the window. */
  278.  
  279. int    ScrollAll = NO;
  280.  
  281. private void
  282. UpdWindow(w, start)
  283. register Window    *w;
  284. int    start;
  285. {
  286.     Line    *lp;
  287.     int    i,
  288.         upper,        /* top of window */
  289.         lower,        /* bottom of window */
  290.         strt_col,    /* starting print column of current line */
  291.         ntries = 0;    /* # of tries at updating window */
  292.     register struct scrimage    *des_p,
  293.                     *phys_p;
  294.     Buffer    *bp = w->w_bufp;
  295.  
  296. retry:
  297.     if (w->w_flags & W_CURGONE) {
  298.         w->w_line = bp->b_dot;
  299.         w->w_char = bp->b_char;
  300.     }
  301.     if (w->w_flags & W_TOPGONE)
  302.         CentWind(w);    /* reset topline of screen */
  303.     w->w_flags &= ~(W_CURGONE | W_TOPGONE);
  304.  
  305.     /* make sure that the current line is in the window */
  306.     upper = start;
  307.     lower = upper + w->w_height - 1;    /* don't include modeline */
  308.     for (i = upper, lp = w->w_top; i < lower && lp != 0; lp = lp->l_next, i++)
  309.         if (lp == w->w_line)
  310.             break;
  311.     if (i == lower || lp == 0) {
  312.         ntries += 1;
  313.         if (ntries == 1) {
  314.             CalcWind(w);
  315.             goto retry;
  316.         } else if (ntries == 2) {
  317.             w->w_top = w->w_line = w->w_bufp->b_first;
  318.             writef("\rERROR in redisplay: I got hopelessly lost!");
  319.             dobell(2);
  320.             goto retry;
  321.         } else if (ntries == 3) {
  322.             writef("\n\rOops, still lost, quitting ...\r\n");
  323.             finish(1);
  324.         }
  325.     }
  326.  
  327.     /* first do some calculations for the current line */
  328.     {
  329.         int    diff = (w->w_flags & W_NUMLINES) ? 8 : 0,
  330.             end_col;
  331.  
  332.         strt_col = (ScrollAll == YES) ? w->w_LRscroll :
  333.                PhysScreen[i].s_offset;
  334.         end_col = strt_col + (CO - 2) - diff;
  335.         /* Right now we are displaying from strt_col to
  336.            end_col of the buffer line.  These are PRINT
  337.            columns, not actual characters. */
  338.         w->w_dotcol = find_pos(w->w_line, w->w_char);
  339.         /* if the new dotcol is out of range, reselect
  340.            a horizontal window */
  341.         if ((PhysScreen[i].s_offset == -1) ||
  342.             (w->w_dotcol < strt_col) ||
  343.             (w->w_dotcol >= end_col)) {
  344.             if (w->w_dotcol < ((CO - 2) - diff))
  345.                 strt_col = 0;
  346.             else
  347.                 strt_col = w->w_dotcol - (CO / 2);
  348.             if (ScrollAll == YES) {
  349.                 if (w->w_LRscroll != strt_col)
  350.                     UpdModLine = YES;
  351.                 w->w_LRscroll = strt_col;
  352.             }
  353.         }
  354.         w->w_dotline = i;
  355.         w->w_dotcol += diff;
  356.     }
  357.  
  358.     des_p = &DesiredScreen[upper];
  359.     phys_p = &PhysScreen[upper];
  360.     for (i = upper, lp = w->w_top; lp != 0 && i < lower; i++, des_p++, phys_p++, lp = lp->l_next) {
  361.         des_p->s_window = w;
  362.         des_p->s_lp = lp;
  363.         des_p->s_id = lp->l_dline & ~DIRTY;
  364.         des_p->s_flags = isdirty(lp) ? L_MOD : 0;
  365.         if (w->w_flags & W_NUMLINES)
  366.             des_p->s_vln = w->w_topnum + (i - upper);
  367.         else
  368.             des_p->s_vln = 0;
  369.  
  370.         if (lp == w->w_line)
  371.             des_p->s_offset = strt_col;
  372.         else
  373.             des_p->s_offset = w->w_LRscroll;
  374.     }
  375.  
  376.     /* Is structure assignment faster than copy each field separately? */
  377.     if (i < lower) {
  378.         static const struct scrimage    dirty_plate = { 0, DIRTY, 0, 0, 0, 0 },
  379.                     clean_plate = { 0, 0, 0, 0, 0, 0 };
  380.  
  381.         for (; i < lower; i++, des_p++, phys_p++)
  382.             if (phys_p->s_id != 0)
  383.                 *des_p = dirty_plate;
  384.             else
  385.                 *des_p = clean_plate;
  386.     }
  387.  
  388.     des_p->s_window = w;
  389.     des_p->s_flags = 0;
  390.     if (((des_p->s_id = (int) w->w_bufp) != phys_p->s_id) || UpdModLine)
  391.         des_p->s_flags = MODELINE | DIRTY;
  392. #ifdef MAC
  393.     if (UpdModLine)
  394.         Modechange = 1;
  395.     if (w == curwind && w->w_control)
  396.         SetScrollBar(w->w_control);
  397. #endif
  398. }
  399.  
  400. /* Write whatever is in mesgbuf (maybe we are Asking, or just printed
  401.    a message).  Turns off the UpdateMesg line flag. */
  402.  
  403. void
  404. DrawMesg(abortable)
  405. int    abortable;
  406. {
  407. #ifndef MAC        /* same reason as in redisplay() */
  408.     if (charp())
  409.         return;
  410. #endif
  411.     i_set(ILI, 0);
  412.     if (swrite(mesgbuf, NO, abortable)) {
  413.         cl_eol();
  414.         UpdMesg = 0;
  415.     }
  416.     flusho();
  417. }
  418.  
  419. /* Goto the current position in the current window.  Presumably redisplay()
  420.    has already been called, and curwind->{w_dotline,w_dotcol} have been set
  421.    correctly. */
  422.  
  423. private void
  424. GotoDot()
  425. {
  426.     if (InputPending)
  427.         return;
  428.     Placur(curwind->w_dotline, curwind->w_dotcol -
  429.                 PhysScreen[curwind->w_dotline].s_offset);
  430.     flusho();
  431. }
  432.  
  433. private int
  434. UntilEqual(start)
  435. register int    start;
  436. {
  437.     register struct scrimage    *des_p = &DesiredScreen[start],
  438.                     *phys_p = &PhysScreen[start];
  439.  
  440.     while ((start < ILI) && (des_p->s_id != phys_p->s_id)) {
  441.         des_p += 1;
  442.         phys_p += 1;
  443.         start += 1;
  444.     }
  445.  
  446.     return start;
  447. }
  448.  
  449. /* Calls the routine to do the physical changes, and changes PhysScreen to
  450.    reflect those changes. */
  451.  
  452. private int
  453. AddLines(at, num)
  454. register int    at,
  455.         num;
  456. {
  457.     register int    i;
  458.     int    bottom = UntilEqual(at + num);
  459.  
  460.     if (num == 0 || num >= ((bottom - 1) - at))
  461.         return NO;                /* we did nothing */
  462.     v_ins_line(num, at, bottom - 1);
  463.  
  464.     /* Now change PhysScreen to account for the physical change. */
  465.  
  466.     for (i = bottom - 1; i - num >= at; i--)
  467.         PhysScreen[i] = PhysScreen[i - num];
  468.     for (i = 0; i < num; i++)
  469.         PhysScreen[at + i].s_id = 0;
  470.     return YES;                    /* we did something */
  471. }
  472.  
  473. private int
  474. DelLines(at, num)
  475. register int    at,
  476.         num;
  477. {
  478.     register int    i;
  479.     int    bottom = UntilEqual(at + num);
  480.  
  481.     if (num == 0 || num >= ((bottom - 1) - at))
  482.         return NO;
  483.     v_del_line(num, at, bottom - 1);
  484.  
  485.     for (i = at; num + i < bottom; i++)
  486.         PhysScreen[i] = PhysScreen[num + i];
  487.     for (i = bottom - num; i < bottom; i++)
  488.         PhysScreen[i].s_id = 0;
  489.     return YES;
  490. }
  491.  
  492. /* Update line linenum in window w.  Only set PhysScreen to DesiredScreen
  493.    if the swrite or cl_eol works, that is nothing is interupted by
  494.    characters typed. */
  495.  
  496. private void
  497. UpdLine(linenum)
  498. register int    linenum;
  499. {
  500.     register struct scrimage    *des_p = &DesiredScreen[linenum];
  501.     register Window    *w = des_p->s_window;
  502.  
  503.     i_set(linenum, 0);
  504.     if (des_p->s_flags & MODELINE)
  505.         ModeLine(w);
  506.     else if (des_p->s_id) {
  507.         des_p->s_lp->l_dline &= ~DIRTY;
  508.         des_p->s_flags &= ~(DIRTY | L_MOD);
  509. #ifdef ID_CHAR
  510.         if (!UseIC && (w->w_flags & W_NUMLINES))
  511. #else
  512.         if (w->w_flags & W_NUMLINES)
  513. #endif
  514.             (void) swrite(sprint("%6d  ", des_p->s_vln), NO, YES);
  515.  
  516. #ifdef ID_CHAR
  517.         if (UseIC) {
  518.             char    outbuf[MAXCOLS],
  519.                 *lptr;
  520.             int    fromcol = (w->w_flags & W_NUMLINES) ? 8 : 0;
  521.  
  522.             if (w->w_flags & W_NUMLINES)
  523.                 swritef(outbuf, "%6d  ", des_p->s_vln);
  524.             lptr = lcontents(des_p->s_lp);
  525.             DeTab(des_p->s_offset, lptr, outbuf + fromcol,
  526.                 (sizeof outbuf) - 1 - fromcol,
  527.                 des_p->s_window->w_flags & W_VISSPACE);
  528.             if (IDchar(outbuf, linenum, 0))
  529.                 PhysScreen[linenum] = *des_p;
  530.             else if (i_set(linenum, 0), swrite(outbuf, NO, YES))
  531.                 do_cl_eol(linenum);
  532.             else
  533.                 PhysScreen[linenum].s_id = -1;
  534.         } else
  535. #endif /* ID_CHAR */
  536.             if (BufSwrite(linenum))
  537.             do_cl_eol(linenum);
  538.         else
  539.             PhysScreen[linenum].s_id = -1;
  540.     } else if (PhysScreen[linenum].s_id)    /* not the same ... make sure */
  541.         do_cl_eol(linenum);
  542. }
  543.  
  544. private void
  545. do_cl_eol(linenum)
  546. register int    linenum;
  547. {
  548.     cl_eol();
  549.     PhysScreen[linenum] = DesiredScreen[linenum];
  550. }
  551.  
  552. #ifdef ID_CHAR
  553.  
  554. /* From here to the end of the file is code that tries to utilize the
  555.    insert/delete character feature on some terminals.  It is very confusing
  556.    and not so well written code, AND there is a lot of it.  You may want
  557.    to use the space for something else. */
  558.  
  559. int    IN_INSmode = 0;
  560.  
  561. int    UseIC;
  562.  
  563. int    DClen,
  564.     MDClen,
  565.     IClen,
  566.     MIClen,
  567.     IMlen,
  568.     CElen;
  569.  
  570. void
  571. disp_opt_init()
  572. {
  573.     DClen = DC ? strlen(DC) : 0;
  574.     MDClen = M_DC ? strlen(M_DC) : 9999;
  575.     IClen = IC ? strlen(IC) : 0;
  576.     MIClen = M_IC ? strlen(M_IC) : 9999;
  577.     IMlen = IM ? strlen(IM) : 0;
  578.     CElen = CE ? strlen(CE) : 0;
  579.  
  580.     UseIC = (IC || IM || M_IC);
  581. }
  582.  
  583. void
  584. INSmode(on)
  585. int    on;
  586. {
  587.     if (on && !IN_INSmode) {
  588.         putpad(IM, 1);
  589.         IN_INSmode = YES;
  590.     } else if (!on && IN_INSmode) {
  591.         putpad(EI, 1);
  592.         IN_INSmode = NO;
  593.     }
  594. }
  595.  
  596. private void
  597. DeTab(s_offset, buf, outbuf, limit, visspace)
  598. int    s_offset;
  599. register char    *buf;
  600. char    *outbuf;
  601. size_t    limit;
  602. int    visspace;
  603. {
  604.     register char    *phys_p = outbuf,
  605.             c;
  606.     register int    pos = 0;
  607.     char        *limitp = &outbuf[limit];
  608.  
  609. #define OkayOut(ch)    { \
  610.     if ((pos++ >= s_offset) && (phys_p < limitp)) \
  611.         *phys_p++ = (ch); \
  612. }
  613.  
  614.     while ((c = *buf++) != '\0') {
  615.         if (c == '\t') {
  616.             int    nchars = (tabstop - (pos % tabstop));
  617.  
  618.             if (visspace) {
  619.                 OkayOut('>');
  620.                 nchars -= 1;
  621.             }
  622.             while (--nchars >= 0)
  623.                 OkayOut(' ');
  624.  
  625.         } else if (isctrl(c)) {
  626.             OkayOut('^');
  627.             OkayOut(c == 0177 ? '?' : c + '@');
  628.         } else {
  629.             if (visspace && c == ' ')
  630.                 c = '_';
  631.             OkayOut(c);
  632.         }
  633.         if (pos - s_offset >= CO) {
  634.             phys_p = &outbuf[CO - 1];
  635.             *phys_p++ = '!';
  636.             break;
  637.         }
  638.     }
  639.     *phys_p = 0;
  640.  
  641. #undef    OkayOut
  642. }
  643.  
  644. /* ID character routines full of special cases and other fun stuff like that.
  645.    It actually works though ...
  646.  
  647.     Returns Non-Zero if you are finished (no differences left). */
  648.  
  649. private int
  650. IDchar(new, lineno, col)
  651. register char    *new;
  652. int    lineno,
  653.     col;
  654. {
  655.     register int    i;
  656.     int    j,
  657.         oldlen,
  658.         NumSaved;
  659.     register struct screenline    *sline = &Screen[lineno];
  660.  
  661.     oldlen = sline->s_length - sline->s_line;
  662.  
  663.     for (i = col; i < oldlen && new[i] != 0; i++)
  664.         if (sline->s_line[i] != new[i])
  665.             break;
  666.     if (new[i] == 0 || i == oldlen)
  667.         return (new[i] == 0 && i == oldlen);
  668.  
  669.     for (j = i + 1; j < oldlen && new[j]; j++) {
  670.         if (new[j] == sline->s_line[i]) {
  671.             NumSaved = IDcomp(new + j, sline->s_line + i,
  672.                     (int)strlen(new)) + NumSimilar(new + i,
  673.                         sline->s_line + i, j - i);
  674.             if (OkayInsert(NumSaved, j - i)) {
  675.                 InsChar(lineno, i, j - i, new);
  676.                 return(IDchar(new, lineno, j));
  677.             }
  678.         }
  679.     }
  680.  
  681.     for (j = i + 1; j < oldlen && new[i]; j++) {
  682.         if (new[i] == sline->s_line[j]) {
  683.             NumSaved = IDcomp(new + i, sline->s_line + j,
  684.                     oldlen - j);
  685.             if (OkayDelete(NumSaved, j - i, new[oldlen] == 0)) {
  686.                 DelChar(lineno, i, j - i);
  687.                 return(IDchar(new, lineno, j));
  688.             }
  689.         }
  690.     }
  691.     return 0;
  692. }
  693.  
  694. private int
  695. NumSimilar(s, t, n)
  696. register char    *s,
  697.         *t;
  698. int    n;
  699. {
  700.     register int    num = 0;
  701.  
  702.     while (n--)
  703.         if (*s++ == *t++)
  704.             num += 1;
  705.     return num;
  706. }
  707.  
  708. private int
  709. IDcomp(s, t, len)
  710. register char    *s,
  711.         *t;
  712. int    len;
  713. {
  714.     register int    i;
  715.     int    num = 0,
  716.         nonspace = 0;
  717.     char    c;
  718.  
  719.     for (i = 0; i < len; i++) {
  720.         if ((c = *s++) != *t++)
  721.             break;
  722.         if (c != ' ')
  723.             nonspace++;
  724.         if (nonspace)
  725.             num += 1;
  726.     }
  727.  
  728.     return num;
  729. }
  730.  
  731. private int
  732. OkayDelete(Saved, num, samelength)
  733. int    Saved,
  734.     num,
  735.     samelength;
  736. {
  737.     /* If the old and the new are the same length, then we don't
  738.      * have to clear to end of line.  We take that into consideration.
  739.      */
  740.     return ((Saved + (!samelength ? CElen : 0))
  741.         > min(MDClen, DClen * num));
  742. }
  743.  
  744. private int
  745. OkayInsert(Saved, num)
  746. int    Saved,
  747.     num;
  748. {
  749.     register int    n = 0;
  750.  
  751.     if (IC)        /* Per character prefixes */
  752.         n = min(num * IClen, MIClen);
  753.  
  754.     if (IM && !IN_INSmode) {
  755.         /* Good terminal.  Fewer characters in this case */
  756.         n += IMlen;
  757.     }
  758.  
  759.     n += num;    /* The characters themselves */
  760.  
  761.     return Saved > n;
  762. }
  763.  
  764. private void
  765. DelChar(lineno, col, num)
  766. int    lineno,
  767.     col,
  768.     num;
  769. {
  770.     register char    *from,
  771.             *to;
  772.     register int    i;
  773.     struct screenline *sp = (&Screen[lineno]);
  774.  
  775.     Placur(lineno, col);
  776.     if (M_DC && num > 1) {
  777.         putargpad(M_DC, num, num);
  778.     } else {
  779.         for (i = num; --i >= 0; )
  780.             putpad(DC, 1);
  781.     }
  782.  
  783.     to = sp->s_line + col;
  784.     from = to + num;
  785.  
  786.     byte_copy(from, to, (size_t) (sp->s_length - from + 1));
  787.     clrline(sp->s_length - num, sp->s_length);
  788.     sp->s_length -= num;
  789. }
  790.  
  791. private void
  792. InsChar(lineno, col, num, new)
  793. int    lineno,
  794.     col,
  795.     num;
  796. char    *new;
  797. {
  798.     register char    *sp1,
  799.             *sp2,    /* To push over the array. */
  800.             *sp3;    /* Last character to push over. */
  801.     int    i;
  802.  
  803.     i_set(lineno, 0);
  804.     sp2 = Curline->s_length + num;
  805.  
  806.     if (sp2 >= cursend) {
  807.         i_set(lineno, CO - num - 1);
  808.         cl_eol();
  809.         sp2 = cursend - 1;
  810.     }
  811.     Curline->s_length = sp2;
  812.     sp1 = sp2 - num;
  813.     sp3 = Curline->s_line + col;
  814.  
  815.     while (sp1 >= sp3)
  816.         *sp2-- = *sp1--;
  817.  
  818.     new += col;
  819.     byte_copy(new, sp3, (size_t) num);
  820.     /* The internal screen is correct, and now we have to do
  821.        the physical stuff. */
  822.  
  823.     Placur(lineno, col);
  824.     if (IM) {
  825.         if (!IN_INSmode)
  826.             INSmode(1);
  827.     } else if (M_IC && num > 1) {
  828.         putargpad(M_IC, num, num);
  829.     } else if (IC) {
  830.         for (i = 0; i < num; i++)
  831.             putpad(IC, 1);
  832.     }
  833.     for (i = 0; i < num; i++) {
  834.         jputchar(new[i]);
  835.         if (IN_INSmode)
  836.             putpad(IP, 1);
  837.     }
  838.     CapCol += num;
  839. }
  840.  
  841. #endif /* ID_CHAR */
  842.  
  843. #ifdef UNIX        /* obviously ... no mail today if not Unix*/
  844.  
  845. /* chkmail() returns nonzero if there is new mail since the
  846.    last time we checked. */
  847.  
  848. char    Mailbox[FILESIZE];    /* initialized in main */
  849. int    MailInt = 60;        /* check no more often than 60 seconds */
  850. #ifdef BIFF
  851. int    BiffChk = NO;        /* whether to turn off biff while in JOVE */
  852. #endif
  853.  
  854. int
  855. chkmail(force)
  856. int    force;
  857. {
  858.     time_t    now;
  859.     static int    state = NO;    /* assume unknown */
  860.     static time_t    last_chk = 0,
  861.             mbox_time = 0;
  862.     struct stat    stbuf;
  863.  
  864.     if (MailInt == 0)
  865.         return NO;
  866.     time(&now);
  867.     if ((force == NO) && (now < last_chk + MailInt))
  868.         return state;
  869.     last_chk = now;
  870.     if (stat(Mailbox, &stbuf) < 0) {
  871.         state = NO;        /* no mail */
  872.         return NO;
  873.     }
  874.     if (((stbuf.st_atime > stbuf.st_mtime) &&
  875.          (stbuf.st_atime > mbox_time)) ||
  876.         (stbuf.st_size == 0)) {
  877.         mbox_time = stbuf.st_atime;
  878.         state = NO;
  879.     } else if (stbuf.st_mtime > mbox_time) {
  880.         if (mbox_time > 0)
  881.             dobell(2);        /* announce the change */
  882.         mbox_time = stbuf.st_mtime;
  883.         state = YES;
  884.     }
  885.     return state;
  886. }
  887.  
  888. #endif /* UNIX */
  889.  
  890. /* Print the mode line. */
  891.  
  892. private char    *mode_p,
  893.         *mend_p;
  894. int    BriteMode = 1;        /* modeline should standout */
  895.  
  896. private void
  897. mode_app(str)
  898. register const char    *str;
  899. {
  900.     while ((mode_p < mend_p) && (*mode_p++ = *str++)!='\0')
  901.         ;
  902.     mode_p -= 1;    /* back over the null */
  903. }
  904.  
  905. char    ModeFmt[120] = "%3c %w %[%sJOVE (%M)   Buffer: %b  \"%f\" %]%s%m*- %((%t)%s%)%e";
  906.  
  907. private void
  908. ModeLine(w)
  909. register Window    *w;
  910. {
  911.     int    n,
  912.         ign_some = NO,
  913.         glue = 0;
  914.     char    line[MAXCOLS],
  915.         *fmt = ModeFmt,
  916.         fillc,
  917.         c;
  918.     register Buffer    *thisbuf = w->w_bufp;
  919.     register Buffer *bp;
  920.  
  921.     mode_p = line;
  922.     mend_p = &line[(sizeof line) - 1];
  923.  
  924. #ifdef IBMPC
  925.     /* very subtle - don't mess up attributes too much */
  926.     fillc = '-';
  927. #else /* !IBMPC */
  928. #  ifdef MAC
  929.     fillc = '_';    /* looks better on a Mac */
  930. #  else /* !MAC */
  931.     if (SO == 0)
  932.         BriteMode = 0;
  933.     fillc = BriteMode ? ' ' : '-';
  934. #  endif /* !MAC */
  935. #endif /* !IBMPC */
  936.  
  937.     while ((c = *fmt++)!='\0' && mode_p<mend_p) {
  938.         if (c != '%') {
  939.             if (c == '\\')
  940.                 if ((c = *fmt++) == '\0')
  941.                     break;
  942.             if (!ign_some)
  943.                 *mode_p++ = c;
  944.             continue;
  945.         }
  946.         if ((c = *fmt++) == '\0')    /* char after the '%' */
  947.             break;
  948.         if (ign_some && c != ')')
  949.             continue;
  950.         n = 1;
  951.         if (c >= '0' && c <= '9') {
  952.             n = 0;
  953.             while (c >= '0' && c <= '9') {
  954.                 n = n * 10 + (c - '0');
  955.                 c = *fmt++;
  956.             }
  957.             if (c == '\0')
  958.                 break;
  959.         }
  960.         switch (c) {
  961.         case '(':
  962.             if (w->w_next != fwind)    /* Not bottom window. */
  963.                 ign_some = YES;
  964.             break;
  965.  
  966.         case ')':
  967.             ign_some = NO;
  968.             break;
  969.  
  970.         case '[':
  971.         case ']':
  972.             for (n=RecDepth; n>0 && mode_p<mend_p; n--)
  973.                 *mode_p++ = c;
  974.             break;
  975.  
  976. #ifdef UNIX
  977.         case 'C':    /* check mail here */
  978.             if (chkmail(NO) == YES)
  979.                 mode_app("[New mail]");
  980.             break;
  981.  
  982. #endif /* UNIX */
  983.  
  984.         case 'M':
  985.             {
  986.             static const char    *const mmodes[] = {
  987.                 "Fundamental ",
  988.                 "Text ",
  989.                 "C ",
  990. #ifdef LISP
  991.                 "Lisp ",
  992. #endif
  993.                 0
  994.             };
  995.  
  996.             mode_app(mmodes[thisbuf->b_major]);
  997.  
  998.             if (BufMinorMode(thisbuf, Fill))
  999.                 mode_app("Fill ");
  1000.             if (BufMinorMode(thisbuf, Abbrev))
  1001.                 mode_app("Abbrev ");
  1002.             if (BufMinorMode(thisbuf, OverWrite))
  1003.                 mode_app("OvrWt ");
  1004.             if (BufMinorMode(thisbuf, Indent))
  1005.                 mode_app("AI ");
  1006.             if (BufMinorMode(thisbuf, ReadOnly))
  1007.                 mode_app("RO ");
  1008.             if (InMacDefine)
  1009.                 mode_app("Def ");
  1010.             mode_p -= 1;    /* Back over the extra space. */
  1011.             break;
  1012.             }
  1013.  
  1014.         case 'c':
  1015.             while (--n>=0 && mode_p<mend_p)
  1016.                 *mode_p++ = fillc;
  1017.             break;
  1018.  
  1019.         case 'd':    /* print working directory */
  1020.             mode_app(pr_name(pwd(), YES));
  1021.             break;
  1022.  
  1023.         case 'e':    /* stretchable glue */
  1024.             *mode_p++ = '\0';    /* glue marker */
  1025.             glue++;
  1026.             break;
  1027.  
  1028.         case 'b':
  1029.             mode_app(thisbuf->b_name);
  1030.             break;
  1031.  
  1032.         case 'f':
  1033.         case 'F':
  1034.             if (thisbuf->b_fname == 0)
  1035.                 mode_app("[No file]");
  1036.             else {
  1037.                 if (c == 'f')
  1038.                     mode_app(pr_name(thisbuf->b_fname, YES));
  1039.                 else
  1040.                     mode_app(basename(thisbuf->b_fname));
  1041.             }
  1042.             break;
  1043.  
  1044. #ifdef LOAD_AV
  1045.         case 'l':
  1046.             {
  1047.             double    theavg;
  1048.             char    minibuf[10];
  1049.  
  1050.             get_la(&theavg);
  1051.             theavg += .005;    /* round to nearest .01 */
  1052.             swritef(minibuf, "%d.%02d",
  1053.                    (int) theavg,
  1054.                    (int)((theavg - (int) theavg) * 100));
  1055.             mode_app(minibuf);
  1056.             break;
  1057.             }
  1058. #endif
  1059.  
  1060.         case 'm':
  1061.             {
  1062.             char    yea = (*fmt == '\0') ? '*' : *fmt++;
  1063.             char    nay = (*fmt == '\0') ? ' ' : *fmt++;
  1064.  
  1065.             *mode_p++ = IsModified(w->w_bufp) ? yea : nay;
  1066.             break;
  1067.             }
  1068.  
  1069.         case 'n':
  1070.             {
  1071.             char    tmp[16];
  1072.             for (bp = world, n = 1; bp != 0; bp = bp->b_next, n++)
  1073.                 if (bp == thisbuf)
  1074.                     break;
  1075.  
  1076.             swritef(tmp, "%d", n);
  1077.             mode_app(tmp);
  1078.             break;
  1079.             }
  1080.  
  1081. #ifdef IPROCS
  1082.         case 'p':
  1083.             if (thisbuf->b_type == B_PROCESS) {
  1084.                 char    tmp[40];
  1085.                 Process    *p = thisbuf->b_process;
  1086.  
  1087.                 swritef(tmp, "(%s%s)",
  1088.                     ((p == 0 || p->p_dbx_mode == NO)
  1089.                      ? "" : "DBX "),
  1090.                     ((p == 0) ? "No process" :
  1091.                      pstate(p)));
  1092.                 mode_app(tmp);
  1093.             }
  1094.             break;
  1095. #endif
  1096.  
  1097.         case 's':
  1098.             if (mode_p[-1] != ' ')
  1099.                 *mode_p++ = ' ';
  1100.             break;
  1101.  
  1102.         case 't':
  1103.             {
  1104.             char    timestr[12];
  1105.  
  1106.             mode_app(get_time((time_t *) 0, timestr, 11, 16));
  1107.             break;
  1108.             }
  1109.  
  1110.         case 'w':
  1111.             if (w->w_LRscroll > 0)
  1112.                 mode_app(">");
  1113.             break;
  1114.         }
  1115.     }
  1116.  
  1117.     /* Glue (Knuth's term) is a field that expands to fill
  1118.      * any leftover space.  Multiple glue fields compete
  1119.      * on an equal basis.  This is a generalization of a
  1120.      * mechanism to allow centring and right-justification.
  1121.      * The original meaning of %e (fill the rest of the
  1122.      * line) has also been generalized.  %e can now
  1123.      * meaningfully be used 0 or more times.
  1124.      */
  1125.  
  1126.     if  (glue) {
  1127.         /* 2 space pad plus padding for magic cookies */
  1128.         register char    *to = &line[CO - 2 - (2 * SG)],
  1129.                 *from = mode_p;
  1130.  
  1131.         if (to < from)
  1132.             to = from;
  1133.         mode_p = to;
  1134.         while (from != line) {
  1135.             if ((*--to = *--from) == '\0') {
  1136.                 register int    portion = (to-from) / glue;
  1137.  
  1138.                 glue--;
  1139.                 *to = fillc;
  1140.                 while (--portion >= 0)
  1141.                     *--to = fillc;
  1142.             }
  1143.         }
  1144.     }
  1145.  
  1146.     *mode_p = 0;
  1147.  
  1148.     /* Highlight mode line. */
  1149.     if (BriteMode) {
  1150. #ifdef ID_CHAR
  1151.         if (IN_INSmode)
  1152.             INSmode(0);
  1153. #endif
  1154.         SO_on();
  1155.     }
  1156.     if (swrite(line, BriteMode, YES))
  1157.         do_cl_eol(i_line);
  1158.     else
  1159.         UpdModLine = 1;
  1160.     if (BriteMode)
  1161.         SO_off();
  1162. }
  1163.  
  1164. private void
  1165. v_clear(line1, line2)
  1166. register int    line1;
  1167. int    line2;
  1168. {
  1169.     register struct scrimage    *phys_p, *des_p;
  1170.  
  1171.     phys_p = &PhysScreen[line1];
  1172.     des_p = &DesiredScreen[line1];
  1173.  
  1174.     while (line1 <= line2) {
  1175.         i_set(line1, 0);
  1176.         cl_eol();
  1177.         phys_p->s_id = des_p->s_id = 0;
  1178.         phys_p += 1;
  1179.         des_p += 1;
  1180.         line1 += 1;
  1181.     }
  1182. }
  1183.  
  1184. /* This tries to place the current line of the current window in the
  1185.    center of the window, OR to place it at the arg'th line of the window.
  1186.    This also causes the horizontal position of the line to be centered,
  1187.    if the line needs scrolling, or moved all the way back to the left,
  1188.    if that's possible. */
  1189. void
  1190. RedrawDisplay()
  1191. {
  1192.     int    line;
  1193.     Line    *newtop = prev_line((curwind->w_line = curline), is_an_arg() ?
  1194.                 arg_value() : HALF(curwind));
  1195.  
  1196.     if ((line = in_window(curwind, curwind->w_line)) != -1)
  1197.         PhysScreen[line].s_offset = -1;
  1198.     if (newtop == curwind->w_top)
  1199.         v_clear(FLine(curwind), FLine(curwind) + SIZE(curwind));
  1200.     else
  1201.         SetTop(curwind, newtop);
  1202. }
  1203.  
  1204. void
  1205. ClAndRedraw()
  1206. {
  1207.     cl_scr(YES);
  1208. }
  1209.  
  1210. void
  1211. NextPage()
  1212. {
  1213.     Line    *newline;
  1214.  
  1215.     if (Asking)
  1216.         return;
  1217.     if (arg_value() < 0) {
  1218.         negate_arg_value();
  1219.         PrevPage();
  1220.         return;
  1221.     }
  1222.     if (arg_type() == YES)
  1223.         UpScroll();
  1224.     else {
  1225.         if (in_window(curwind, curwind->w_bufp->b_last) != -1) {
  1226.             rbell();
  1227.             return;
  1228.         }
  1229.         newline = next_line(curwind->w_top, max(1, SIZE(curwind) - 1));
  1230.         SetTop(curwind, curwind->w_line = newline);
  1231.         if (curwind->w_bufp == curbuf)
  1232.             SetLine(newline);
  1233.     }
  1234. }
  1235.  
  1236. #ifdef MSDOS        /* kg */
  1237.  
  1238. void
  1239. PageScrollUp()
  1240. {
  1241.     int i, n;
  1242.  
  1243.     n = max(1, SIZE(curwind) - 1);
  1244.     for (i=0; i<n; i++) {
  1245.         UpScroll();
  1246.         redisplay();
  1247.     }
  1248. }
  1249.  
  1250. void
  1251. PageScrollDown()
  1252. {
  1253.     int i, n;
  1254.  
  1255.     n = max(1, SIZE(curwind) - 1);
  1256.     for (i=0; i<n; i++) {
  1257.         DownScroll();
  1258.         redisplay();
  1259.     }
  1260. }
  1261. #endif /* MSDOS */
  1262.  
  1263. void
  1264. PrevPage()
  1265. {
  1266.     Line    *newline;
  1267.  
  1268.     if (Asking)
  1269.         return;
  1270.     if (arg_value() < 0) {
  1271.         negate_arg_value();
  1272.         NextPage();
  1273.         return;
  1274.     }
  1275.     if (arg_type() == YES)
  1276.         DownScroll();
  1277.     else {
  1278.         newline = prev_line(curwind->w_top, max(1, SIZE(curwind) - 1));
  1279.         SetTop(curwind, curwind->w_line = newline);
  1280.         if (curwind->w_bufp == curbuf)
  1281.             SetLine(newline);
  1282.     }
  1283. }
  1284.  
  1285. void
  1286. UpScroll()
  1287. {
  1288.     SetTop(curwind, next_line(curwind->w_top, arg_value()));
  1289.     if ((curwind->w_bufp == curbuf) &&
  1290.         (in_window(curwind, curline) == -1))
  1291.         SetLine(curwind->w_top);
  1292. }
  1293.  
  1294. void
  1295. DownScroll()
  1296. {
  1297.     SetTop(curwind, prev_line(curwind->w_top, arg_value()));
  1298.     if ((curwind->w_bufp == curbuf) &&
  1299.         (in_window(curwind, curline) == -1))
  1300.         SetLine(curwind->w_top);
  1301. }
  1302.  
  1303. int    VisBell = NO;
  1304.  
  1305. void
  1306. rbell()
  1307. {
  1308.     RingBell = YES;
  1309. }
  1310.  
  1311. /* Message prints the null terminated string onto the bottom line of the
  1312.    terminal. */
  1313.  
  1314. void
  1315. message(str)
  1316. char    *str;
  1317. {
  1318.     if (InJoverc)
  1319.         return;
  1320.     UpdMesg = YES;
  1321.     errormsg = NO;
  1322.     if (str != mesgbuf)
  1323.         null_ncpy(mesgbuf, str, (sizeof mesgbuf) - 1);
  1324. }
  1325.  
  1326. /* End of Window */
  1327.  
  1328. void
  1329. Eow()
  1330. {
  1331.     if (Asking)
  1332.         return;
  1333.     SetLine(next_line(curwind->w_top, SIZE(curwind) - 1 -
  1334.             min(SIZE(curwind) - 1, arg_value() - 1)));
  1335.     if (!is_an_arg())
  1336.         Eol();
  1337. }
  1338.  
  1339. /* Beginning of Window */
  1340.  
  1341. void
  1342. Bow()
  1343. {
  1344.     if (Asking)
  1345.         return;
  1346.     SetLine(next_line(curwind->w_top, min(SIZE(curwind) - 1, arg_value() - 1)));
  1347. }
  1348.  
  1349. private int    LineNo,
  1350.         last_col,
  1351.         DoAutoNL;
  1352. private Window    *old_wind;    /* save the window we were in BEFORE
  1353.                    before we were called, if UseBuffers
  1354.                    is nonzero */
  1355.  
  1356. int    UseBuffers = FALSE;
  1357. int    TOabort = 0;
  1358.  
  1359. /* This initializes the typeout.  If send-typeout-to-buffers is set
  1360.    the buffer NAME is created (emptied if it already exists) and output
  1361.    goes to the buffer.  Otherwise output is drawn on the screen and
  1362.    erased by TOstop() */
  1363.  
  1364. void
  1365. TOstart(name, auto_newline)
  1366. char    *name;
  1367. int    auto_newline;
  1368. {
  1369.     if (UseBuffers) {
  1370.         old_wind = curwind;
  1371.         pop_wind(name, YES, B_SCRATCH);
  1372.     } else
  1373.         DisabledRedisplay = YES;
  1374.     TOabort = LineNo = last_col = 0;
  1375.     DoAutoNL = auto_newline;
  1376. }
  1377.  
  1378. #ifdef    STDARGS
  1379.     void
  1380. Typeout(char *fmt, ...)
  1381. #else
  1382.     /*VARARGS1*/ void
  1383. Typeout(fmt, va_alist)
  1384.     char    *fmt;
  1385.     va_dcl
  1386. #endif
  1387. {
  1388.     if (TOabort)
  1389.         return;
  1390.  
  1391.     if (!UseBuffers && (LineNo == ILI - 1)) {
  1392.         register int    c;
  1393.  
  1394.         LineNo = 0;
  1395.         last_col = 0;
  1396.         f_mess("--more--");
  1397.         if ((c = jgetchar()) != ' ') {
  1398.             TOabort = YES;
  1399.             if (c != AbortChar && c != RUBOUT)
  1400.                 Ungetc(c);
  1401.             f_mess(NullStr);
  1402.             return;
  1403.         }
  1404.         f_mess(NullStr);
  1405.     }
  1406.  
  1407.     if (fmt) {
  1408.         char    string[132];
  1409.         va_list    ap;
  1410.  
  1411.         va_init(ap, fmt);
  1412.         format(string, sizeof string, fmt, ap);
  1413.         va_end(ap);
  1414.         if (UseBuffers)
  1415.             ins_str(string, NO);
  1416.         else {
  1417.             i_set(LineNo, last_col);
  1418.             (void) swrite(string, NO, YES);
  1419.             last_col = i_col;
  1420.         }
  1421.     }
  1422.     if (!UseBuffers) {
  1423.         PhysScreen[LineNo].s_id = -1;
  1424.         if (fmt == 0 || DoAutoNL == YES) {
  1425.             cl_eol();
  1426.             flusho();
  1427.             LineNo += 1;
  1428.             last_col = 0;
  1429.         }
  1430.     } else if (fmt == 0 || DoAutoNL != 0)
  1431.         ins_str("\n", NO);
  1432. }
  1433.  
  1434. void
  1435. TOstop()
  1436. {
  1437.     int    c;
  1438.  
  1439.     if (UseBuffers) {
  1440.         ToFirst();
  1441.         SetWind(old_wind);
  1442.     } else {
  1443.         if (TOabort) {
  1444.             DisabledRedisplay = NO;
  1445.             return;
  1446.         }
  1447.         if (last_col != 0)
  1448.             Typeout((char *) 0);
  1449.         Typeout("----------");
  1450.         cl_eol();
  1451.         flusho();
  1452.         c = jgetchar();
  1453.         if (c != ' ')
  1454.             Ungetc(c);
  1455.         DisabledRedisplay = NO;
  1456.     }
  1457. }
  1458.